/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.datasource.configuration;

import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.creator.hikaricp.HikariDataSourceCreator;
import com.baomidou.dynamic.datasource.processor.DsJakartaHeaderProcessor;
import com.baomidou.dynamic.datasource.processor.DsJakartaSessionProcessor;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
import java.util.ArrayList;
import java.util.List;
import org.elsfs.cloud.api.datasource.support.DataSourceProperties;
import org.elsfs.cloud.common.datasource.support.JdbcDynamicDataSourceProvider;
import org.elsfs.cloud.common.datasource.support.LastParamDsProcessor;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.BeanFactoryResolver;

/**
 * 多数据源配置
 *
 * @author zeng
 */
@AutoConfigureAfter(
    value = DataSourceAutoConfiguration.class,
    name = "org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration")
public class DatasourceConfiguration {

  @Bean
  @ConfigurationProperties(prefix = "spring.datasource")
  DataSourceProperties dataSourceProperties() {
    return new DataSourceProperties();
  }

  /**
   * 动态数据源提供者
   *
   * @param defaultDataSourceCreator 默认数据源创建器
   * @param properties 数据源配置属性
   * @return 动态数据源提供者
   */
  @Bean
  @ConditionalOnProperty(
      prefix = "spring.datasource",
      name = "dynamic-jdbc-enabled",
      havingValue = "true",
      matchIfMissing = true)
  public JdbcDynamicDataSourceProvider jdbcDynamicDataSourceProvider(
      DefaultDataSourceCreator defaultDataSourceCreator,
      DataSourceProperties properties,
      StringEncryptor stringEncryptor) {
    return new JdbcDynamicDataSourceProvider(defaultDataSourceCreator, properties, stringEncryptor);
  }

  /**
   * 获取数据源处理器
   *
   * @param beanFactory bean工厂
   * @return 数据源处理器
   */
  @Bean
  public DsProcessor dsProcessor(BeanFactory beanFactory) {
    DsProcessor lastParamDsProcessor = new LastParamDsProcessor();
    DsProcessor headerProcessor = new DsJakartaHeaderProcessor();
    DsProcessor sessionProcessor = new DsJakartaSessionProcessor();
    DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
    spelExpressionProcessor.setBeanResolver(new BeanFactoryResolver(beanFactory));
    lastParamDsProcessor.setNextProcessor(headerProcessor);
    headerProcessor.setNextProcessor(sessionProcessor);
    sessionProcessor.setNextProcessor(spelExpressionProcessor);
    return lastParamDsProcessor;
  }

  /**
   * 默认数据源创建器
   *
   * @param hikariDataSourceCreator Hikari数据源创建器
   * @return 默认数据源创建器
   */
  @Bean
  public DefaultDataSourceCreator defaultDataSourceCreator(
      HikariDataSourceCreator hikariDataSourceCreator) {
    DefaultDataSourceCreator defaultDataSourceCreator = new DefaultDataSourceCreator();
    List<DataSourceCreator> creators = new ArrayList<>();
    creators.add(hikariDataSourceCreator);
    defaultDataSourceCreator.setCreators(creators);
    return defaultDataSourceCreator;
  }
}
